package com.zjuee.web.basic.captcha.service.impl;

import cn.hutool.core.util.IdUtil;
import com.zjuee.base.bean.response.CommonResultCode;
import com.zjuee.base.dto.common.basic.CaptchaPointDto;
import com.zjuee.base.exception.BaseException;
import com.zjuee.web.basic.captcha.utils.CaptchaUtil;
import com.zjuee.web.basic.captcha.utils.FontUtil;
import com.zjuee.service.basic.captcha.vo.CaptchaVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 点选文字验证码
 *
 * @author wu liang
 * @since 1.0
 */
@Slf4j
@Service(value = "clickWord")
public class ClickWordCaptchaServiceImpl extends AbstractCaptchaService  {

    @Override
    public String captchaType() {
        return "clickWord";
    }


    /**
     * 获取点选 图片验证码
     */
    @Override
    public CaptchaVo get() {
        HttpServletRequest request =((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        BufferedImage bufferedImage = CaptchaUtil.randomPicClick();
        if (null == bufferedImage) {
            throw new BaseException(CommonResultCode.CAPTCHA_CHECK_ERR, "点选验证码 原图初始化失败");
        }
        CaptchaVo bean = getImageData(bufferedImage);
        if (StringUtils.isBlank(bean.getImageBase64()))
            throw new BaseException(CommonResultCode.CAPTCHA_CHECK_ERR, "点选验证码 生成失败");

        CaptchaVo sessionBean = new CaptchaVo();
        sessionBean.setPointList(bean.getPointList());
        sessionBean.setExpiresTimes(System.currentTimeMillis() + CaptchaUtil.SESSION_CAPT_EXPIRES_TIMES);
        request.getSession().setAttribute(CaptchaUtil.CAPTCHA_SESSION_TEMP_KEY, sessionBean);
        return bean;
    }

    /**
     * 校验 点选验证码 点击节点的有效性
     */
    @Override
    public String check(CaptchaVo params) {
        HttpServletRequest request =((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //取坐标信息
        Object attribute = request.getSession().getAttribute(CaptchaUtil.CAPTCHA_SESSION_TEMP_KEY);
        if (attribute == null)
            throw new BaseException(CommonResultCode.CAPTCHA_CHECK_ERR);
        // 清除session
        request.getSession().removeAttribute(CaptchaUtil.CAPTCHA_SESSION_TEMP_KEY);

        if (!(attribute instanceof CaptchaVo))
            throw new BaseException(CommonResultCode.CAPTCHA_CHECK_ERR);
        CaptchaVo localCaptcha = (CaptchaVo) attribute;
        List<CaptchaPointDto> localPointList = localCaptcha.getPointList();
        if ( localPointList == null || localPointList.isEmpty())
            throw new BaseException(CommonResultCode.CAPTCHA_CHECK_ERR);
        // 校验是否已失效
        if (System.currentTimeMillis() > localCaptcha.getExpiresTimes())
            throw new BaseException(CommonResultCode.CAPTCHA_CHECK_ERR, "验证码已失效，请重新获取");

        // 校验参数中的 操作点数量
        List<CaptchaPointDto> pointList = params.getPointList();
        if (pointList == null || pointList.size() != localPointList.size())
            throw new BaseException(CommonResultCode.CAPTCHA_CHECK_ERR);

        // 校验参数
        for (int i = 0; i < localPointList.size(); i++) {
            CaptchaPointDto localPoint = localPointList.get(i);
            CaptchaPointDto paramPoint = pointList.get(i);
            if (localPoint.getX() - HAN_ZI_SIZE > paramPoint.getX()
                    || localPoint.getX() + HAN_ZI_SIZE < paramPoint.getX()
                    || localPoint.getY() - HAN_ZI_SIZE > paramPoint.getY()
                    || localPoint.getY() + HAN_ZI_SIZE < paramPoint.getY()) {
                throw new BaseException(CommonResultCode.CAPTCHA_CHECK_ERR);
            }
        }

        //校验成功，分发一个token
        String token = IdUtil.simpleUUID();
        CaptchaVo sessionBean = new CaptchaVo();
        sessionBean.setDefaultValue(token);
        sessionBean.setExpiresTimes(System.currentTimeMillis() + CaptchaUtil.SESSION_CAPT_EXPIRES_TIMES);
        request.getSession().setAttribute(CaptchaUtil.CAPTCHA_SESSION_KEY, sessionBean);
        return token;
    }

    /**
     * 在指定原图上 随机绘画多个汉字
     */
    private CaptchaVo getImageData(BufferedImage backgroundImage) {
        List<String> wordList = new ArrayList();
        List<CaptchaPointDto> pointList = new ArrayList();

        Graphics backgroundGraphics = backgroundImage.getGraphics();
        int width = backgroundImage.getWidth();
        int height = backgroundImage.getHeight();

        //设置字体大小，float型
        Font font = FontUtil.getFont_CH(Font.BOLD, HAN_ZI_SIZE);

        //定义随机某一个字不参与校验
        int num = RandomUtils.nextInt(0, wordTotalCount);
        Set<String> currentWords = new HashSet<String>();
        for (int i = 0; i < wordTotalCount; i++) {
            String word;
            do {
                word = getRandomHanzi();
                currentWords.add(word);
            } while (!currentWords.contains(word));

            //随机字体坐标
            CaptchaPointDto point = randomWordPoint(width, height, i, wordTotalCount);
            //随机字体颜色
            backgroundGraphics.setColor(new Color(RandomUtils.nextInt(1, 255), RandomUtils.nextInt(1, 255), RandomUtils.nextInt(1, 255)));
            // backgroundGraphics.setColor(Color.BLACK);

            //设置角度
            AffineTransform affineTransform = new AffineTransform();
            affineTransform.rotate(Math.toRadians(RandomUtils.nextInt(-45, 45)), 0, 0);
            Font rotatedFont = font.deriveFont(affineTransform);
            backgroundGraphics.setFont(rotatedFont);
            backgroundGraphics.drawString(word, point.getX(), point.getY());

            if (num != i) {
                wordList.add(word);
                pointList.add(point);
            }
        }

        Font watermark = FontUtil.getFont_CH(Font.BOLD, waterMarkFontSize);
        backgroundGraphics.setFont(watermark);
        backgroundGraphics.setColor(Color.white);
        backgroundGraphics.drawString(waterMark, width - getEnOrChLength(waterMark), height - waterMarkFontSize + 7);

        //创建合并图片
        BufferedImage combinedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics combinedGraphics = combinedImage.getGraphics();
        combinedGraphics.drawImage(backgroundImage, 0, 0, null);

        CaptchaVo bean = new CaptchaVo();
        bean.setImageBase64(CaptchaUtil.imageToDataUrl(backgroundImage));
        bean.setWorkList(wordList);
        bean.setPointList(pointList);
        return bean;
    }

    /**
     * 随机字体循环排序下标
     *
     * @param imageWidth    图片宽度
     * @param imageHeight   图片高度
     * @param wordSortIndex 字体循环排序下标(i)
     * @param wordCount     字数量
     * @return
     */
    private static CaptchaPointDto randomWordPoint(int imageWidth, int imageHeight, int wordSortIndex, int wordCount) {
        int avgWidth = imageWidth / (wordCount + 1);
        int x, y;
        if (avgWidth < HAN_ZI_SIZE_HALF) {
            x = RandomUtils.nextInt(1 + HAN_ZI_SIZE_HALF, imageWidth);
        } else {
            if (wordSortIndex == 0) {
                x = RandomUtils.nextInt(1 + HAN_ZI_SIZE_HALF, avgWidth * (wordSortIndex + 1) - HAN_ZI_SIZE_HALF);
            } else {
                x = RandomUtils.nextInt(avgWidth * wordSortIndex + HAN_ZI_SIZE_HALF, avgWidth * (wordSortIndex + 1) - HAN_ZI_SIZE_HALF);
            }
        }
        y = RandomUtils.nextInt(HAN_ZI_SIZE, imageHeight - HAN_ZI_SIZE);
        return new CaptchaPointDto(x, y);
    }

}
